import fractal._
import util._
import java.awt.image.BufferedImage;
import java.io._
import javax.imageio._
import scala.actors._, Actor._

object FractalGenerator
{
    private var fractal: Fractal = new Mandelbrot()
    private var numActors = 1
    private var isBW = true
    private var rowsComplete = 0

    def getComplexPlane() = ComplexPlane

    def setFractal(fractal: Fractal) = { this.fractal = fractal }

    def getFractal() = fractal 

    def setNumActors(numActors: Int) = { this.numActors = numActors }

    def getNumActors() = numActors

    def getPercentageComplete() = rowsComplete * 100 / ComplexPlane.HEIGHT

    def makeBW = { isBW = true }

    def makeColor = { isBW = false }

    def saveImage(filePath: String): Unit = 
    {
        var bi = ComplexPlane.getImage()
  	    var outputfile = new File(filePath)
  	    ImageIO.write(bi, "png", outputfile)
    }

    def saveParameters(filePath:String): Unit = 
    {
        var out = new BufferedWriter(new FileWriter(filePath));
        out.write(fractal.name);
        out.newLine();
        for(i <- 0 until fractal.params.length)
        {
	   	    out.write(fractal.params(i));
	        out.newLine();		    
        }
	    out.write("Real Axis: " + ComplexPlane.getRealAxisScale());
	    out.newLine();
	    out.write("Imaginary Axis: " + ComplexPlane.getImaginaryAxisScale());
	    out.newLine();
	    out.write("Center: " + ComplexPlane.getCenter());
	    out.newLine();
	    out.close();
    }

    def generate(): Long =
    {
        var actorsDone = 0
        val numRows = ComplexPlane.HEIGHT/numActors
        val start = (x: Int) => x*numRows
        val end = (x: Int) => if (x < numActors-1) (x+1)*numRows else ComplexPlane.HEIGHT
        var tStart: Long = 0
        var tEnd: Long = 0

        rowsComplete = 0

        tStart = System.currentTimeMillis()

        (0 until numActors).foreach(x => {(new ComputeActor(self, start(x) until end(x))).start()})

        while (actorsDone < numActors) {
            receive {
                case UpdateImage(x, y, color) =>
                        ComplexPlane.getImage().setRGB(x, y, color)
                case UpdatePercent => rowsComplete += 1
                case Completed => actorsDone += 1
            }
        }

        tEnd = System.currentTimeMillis()

        return tEnd - tStart
    }

    private case class UpdateImage(x: Int, y: Int, color: Int)
    private case object UpdatePercent
    private case object Completed

    private class ComputeActor(val mainActor: Actor, val rows: Range)
    extends Actor
    {
        val fractal = FractalGenerator.fractal
        val isBW = FractalGenerator.isBW
        var c: Complex = null
        var color = 0

        def act()
        {
	    for (row <- rows) 
            {
                for (col <- 0 until ComplexPlane.WIDTH)
                {
                    c = ComplexPlane.ptoc(row, col)
                    color = if (isBW) fractal.getBW(c) else fractal.getColor(c)
                    mainActor ! UpdateImage(row, col, color)
                }
                mainActor ! UpdatePercent
            }
            mainActor ! Completed
        }
    }
}
